package org.shiro.config.shiro;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.shiro.basic.entity.SystemUser;
import org.shiro.basic.properties.ShiroPropriety;
import org.shiro.service.SystemUserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

/**
 * 自定义shiro认证器
 *
 * @author LiKun
 * @date 2021/11/2 9:24
 */
@Component
public class CustomizeRealm extends AuthorizingRealm implements Serializable {
    private static final Logger logger = LoggerFactory.getLogger(CustomizeRealm.class);

    @Resource
    private ShiroPropriety shiroPropriety;
    @Resource
    private SystemUserService systemUserService;
    @Resource
    private HashedCredentialsMatcher hashedCredentialsMatcher;

    @PostConstruct
    private void initialize() {
        logger.info("加载自定义认证器CustomizeRealm成功");
        this.setCredentialsMatcher(hashedCredentialsMatcher);
    }

    /**
     * 进行用户权限数据认证
     *
     * @return 用户权限数据
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.setStringPermissions(this.getRoleList());
        authorizationInfo.setRoles(this.getRoleList());
        return authorizationInfo;
    }

    /**
     * 进行用户身份认证
     *
     * @param token 用户身份数据
     *
     * @return AuthenticationInfo 认证结果
     *
     * @throws AuthenticationException 相关异常信息
     */
    @Override
    protected ShiroSimpleAuthenticaInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = token.getPrincipal().toString();
        // 根据用户名称从数据库中读取用户数据；如果数据不存在则抛出 UnknownAccountException 异常
        SystemUser systemUser = systemUserService.findSystemUser(username).orElseThrow(UnknownAccountException::new);

        return this.getSimpleAuthenticationInfo(this.validatedSystemUserState(systemUser));
    }


    /*------------------------------------------------private method--------------------------------------------------*/


    /**
     * 获取用户相关角色信息
     *
     * @return 用户角色信息
     */
    private Set<String> getRoleList() {
        return new HashSet<String>() {{
            add("ADMIN");
        }};
    }

    /**
     * 获取用户身份认证数据
     *
     * @param systemUser 用户账号数据
     *
     * @return 获取用户身份认证数据
     */
    private ShiroSimpleAuthenticaInfo getSimpleAuthenticationInfo(SystemUser systemUser) {
        return new ShiroSimpleAuthenticaInfo(systemUser.getId(), systemUser.getPassword(),
                ByteSource.Util.bytes(shiroPropriety.getSalt()), this.getName());
    }

    /**
     * 校验用户账号状态
     *
     * @param systemUser 用户账号数据
     *
     * @return systemUser 用户账号数据
     */
    private SystemUser validatedSystemUserState(SystemUser systemUser) {
        if (systemUser.isEnabled()) {
            return systemUser;
        }
        throw new LockedAccountException();
    }
}
